// Copyright 2008 Steve Spiller. All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are met:
// 
//    1. Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//    2. Redistributions in binary form must reproduce the above copyright 
//       notice, this list of conditions and the following disclaimer in the 
//       documentation and/or other materials provided with the distribution.
//       
// THIS SOFTWARE IS PROVIDED BY THE KAOS PROJECT ``AS IS'' AND ANY EXPRESS 
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 
// NO EVENT SHALL THE PROJECT OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "Config.h"

const struct CDriverConfig::configInfo CDriverConfig::configValueTable[] =
{
    { __MUD_NAME__, 1, "name", "__MUD_NAME__" },
    { __ADDR_SERVER_IP__, 0, "address server ip", "__ADDR_SERVER_IP__" },
    { __MUD_LIB_DIR__, 1, "mudlib directory", "__MUD_LIB_DIR__" },
    { __BIN_DIR__, 1, "binary directory", "__BIN_DIR__" },
    { __LOG_DIR__, 1, "log directory", "__LOG_DIR__" },
    { __INCLUDE_DIRS__, 1, "include directories", "__INCLUDE_DIRS__" },
    { __SAVE_BINARIES_DIR__, 1, "save binaries directory", "__SAVE_BINARIES_DIR__" },
    { __MASTER_FILE__, 1, "master file", "__MASTER_FILE__" },
    { __SIMUL_EFUN_FILE__, 0, "simulated efun file", "__SIMUL_EFUN_FILE__" },
    { __SWAP_FILE__, 1, "swap file", "__SWAP_FILE__" },
    { __DEBUG_LOG_FILE__, -1, "debug log file", "__DEBUG_LOG_FILE__" },
    { __DEFAULT_ERROR_MESSAGE__, 0, "default error message", "__DEFAULT_ERROR_MESSAGE__" },
    { __DEFAULT_FAIL_MESSAGE__, 0, "default fail message", "__DEFAULT_FAIL_MESSAGE__" },
    { __GLOBAL_INCLUDE_FILE__, 0, "global include file", "__GLOBAL_INCLUDE_FILE__" },
    { __MUD_IP__, 0, "mud ip", "__MUD_IP__" },
    { __MUD_PORT__, 0, "port number", "__MUD_PORT__" },
    { __ADDR_SERVER_PORT__, 0, "address server port", "__ADDR_SERVER_PORT__" },
    { __TIME_TO_CLEAN_UP__, 1, "time to clean up", "__TIME_TO_CLEAN_UP__" },
    { __TIME_TO_RESET__, 1, "time to reset", "__TIME_TO_RESET__" },
    { __TIME_TO_SWAP__, 1, "time to swap", "__TIME_TO_SWAP__" },
    { __INHERIT_CHAIN_SIZE__, 1, "inherit chain size", "__INHERIT_CHAIN_SIZE__" },
    { __MAX_EVAL_COST__, 1, "maximum evaluation cost", "__MAX_EVAL_COST__" },
    { __MAX_ARRAY_SIZE__, 1, "maximum array size", "__MAX_ARRAY_SIZE__" },
    { __MAX_BUFFER_SIZE__, 1, "maximum buffer size", "__MAX_BUFFER_SIZE__" },
    { __MAX_MAPPING_SIZE__, 1, "maximum mapping size", "__MAX_MAPPING_SIZE__" },
    { __MAX_STRING_LENGTH__, 1, "maximum string length", "__MAX_STRING_LENGTH__" },
    { __MAX_BITFIELD_BITS__, 1, "maximum bits in a bitfield", "__MAX_BITFIELD_BITS__" },
    { __MAX_BYTE_TRANSFER__, 1, "maximum byte transfer", "__MAX_BYTE_TRANSFER__" },
    { __MAX_READ_FILE_SIZE__, 1, "maximum read file size", "__MAX_READ_FILE_SIZE__" },
    { __RESERVED_MEM_SIZE__, 0, "reserved size", "__RESERVED_MEM_SIZE__" },
    { __SHARED_STRING_HASH_TABLE_SIZE__, 1, "hash table size", "__SHARED_STRING_HASH_TABLE_SIZE__" },
    { __OBJECT_HASH_TABLE_SIZE__, 1, "object table size", "__OBJECT_HASH_TABLE_SIZE__" } 
};

CDriverConfig * CDriverConfig::m_pInstance       = NULL;
const CHAR      CDriverConfig::runtimeFileName[] = "runtime_config.h";

CDriverConfig::CDriverConfig()
{
    m_intValues.reserve(countIntValues);
    for(INT x = 0; x < countIntValues; x++)
    {
        m_intValues.push_back(-1);
    }

    m_stringValues.reserve(countStringValues);
    for(INT x = 0; x < countStringValues; x++)
    {
        m_stringValues.push_back("Undefined");
    }
}

CDriverConfig::~CDriverConfig()
{
    if(m_pInstance)
    {
        delete m_pInstance;
        m_pInstance = NULL;
    }
}

CDriverConfig* 
CDriverConfig::GetConfig(void)
{
    if(m_pInstance == NULL)
    {
        m_pInstance = new CDriverConfig();
    }

    return m_pInstance;
}

void
CDriverConfig::Load(std::string fileName)
{
    std::vector<std::string> lines;

    m_fileName = fileName;

    //
    // read in each line of the config file
    //
    std::ifstream fd(fileName.c_str());

    while(fd.good())
    {
        std::string line;

        // Pull a line from the file
        getline(fd, line);

        // Ignore empty lines or comments
        if(line.length() == 0 || line[0] == '#' || line[0] == '\n')
        {
            continue;
        }

        lines.push_back(line);
    }

    fd.close();

    //
    // scan the list of lines for each of the config values we need
    //
    for(size_t x = 0; x < (sizeof(configValueTable) / sizeof(struct configInfo)); x++)
    {
        std::vector<std::string>::iterator it;

        for(it = lines.begin(); it != lines.end(); it++)
        {
            size_t index = (*it).find_first_of(':');

            if(index == std::string::npos)
            {
                // Invalid config file line
                // TODO: log this
                continue;
            }

            if(TrimString((*it).substr(0, index)) == configValueTable[x].label)
            {
                std::string value(TrimString((*it).substr(index + 1)));

                if((INT) x < firstIntValue)
                {
                    // it's a string value
                    m_stringValues[configValueTable[x].index] = value;
                }
                else
                {
                    // it's an integer value
                    std::stringstream temp(value);
                    temp >> m_intValues[configValueTable[x].index - firstIntValue];
                }

                break;
            }
        }
    }
}

void
CDriverConfig::Reload()
{
    m_intValues.clear();
    m_stringValues.clear();

    m_intValues.reserve(countIntValues);
    for(INT x = 0; x < countIntValues; x++)
    {
        m_intValues.push_back(-1);
    }

    m_stringValues.reserve(countStringValues);
    for(INT x = 0; x < countStringValues; x++)
    {
        m_stringValues.push_back("Undefined");
    }

    Load(m_fileName);
}

void
CDriverConfig::WriteRuntimeConfig(std::string path)
{
    std::string file = path;
    
    file += "//";
    file += runtimeFileName;

    std::ofstream fd(file.c_str());

    for(size_t x = 0; x < (sizeof(configValueTable) / sizeof(struct configInfo)); x++)
    {
        fd << "#define ";
        fd << configValueTable[x].name;
        fd << " ";
        fd << configValueTable[x].index;
        fd << std::endl;
    }

    fd.close();
}

int
CDriverConfig::GetConfigInt(DriverConfigValue which)
{
    if(which < firstIntValue)
    {
        throw ConfigException("Invalid integer config value.");
    }

    return m_intValues[which];
}

std::string
CDriverConfig::GetConfigString(DriverConfigValue which)
{
    if(which >= firstIntValue)
    {
        throw ConfigException("Invalid string config value.");
    }

    return m_stringValues[which];
}
